package multimanipulators;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;

import java.util.Iterator;
import java.util.List;

import java.util.logging.Level;
import java.util.logging.Logger;

import be.djdb.UnConstruct;


import multimanipulators.interfaces.Finders;
import multimanipulators.interfaces.StreamManipulators;
/**
 * @author Lieven Roegiers
 * @copyright 2009
 * @project javamylibs
 * @from http://javamylibs.googlecode.com/svn/trunk/
**/
@UnConstruct(label= "not working edition lost somtimes data [important bug is found]")
public class StreamMultiManipulatorCR1 implements StreamManipulators , Serializable{
	private int maxlockbufferoffinders=0;//wil zeggen wat de grootste lock is van alle finders
	private static DEBUGMODE mode ;//wil make a full detection of the stream
	public List<Finders>finders;//list of finders
	private Finders finder;//actual finder
	private int charint;//actieve char 
	private boolean ismatch;//wil zeggen wanneer er een positieve match is gevonden
	private int bufferclockLength;//length of buffer
	private char buffer[];//data for use to manipulate
	private int buffersizeSequence;//can make the size bigger by any out index out of bound exception
	public  StreamMultiManipulatorCR1(int buffersizeSequence){
		this(buffersizeSequence,DEBUGMODE.NORMAL);		
	}
	public  StreamMultiManipulatorCR1(int buffersizeSequence,DEBUGMODE mode){
		if (buffersizeSequence>0){
			this.buffersizeSequence=buffersizeSequence;
		}else{
			this.buffersizeSequence=20;
		}	
		this.mode = mode;
	}
	private void init(){
		buffer=null;
		buffer= new char[10];
        ismatch = false;
        bufferclockLength =0;
        maxlockbufferoffinders =0;
        for (Finders finder : finders){
        	finder.init();
        }
    }
	public void findAndReplace(InputStream input,List<Finders>list,OutputStreamWriter Doutstream) throws IOException{
		if (this.mode == DEBUGMODE.FOLLOW||this.mode == DEBUGMODE.DEBUG){	
			Doutstream.append("\n<<DEBUGMODE>>");
			Doutstream.append("\n<DEBUGtag:null(do nothing)>//loss buffer");
			Doutstream.append("\n<DEBUGtag:resize (resize the buffer and append to stream)>//match not found buffer bigger than must to used finder");
			Doutstream.append("\n<DEBUGtag:found (one of the finders has a match wil stop al other start replace de finders wil start after the replace)>//search for match is found");
		}
		if (this.mode == DEBUGMODE.CHARINT){	
			Doutstream.append("\n<<DEBUGMODE>>");
			Doutstream.append("\n<ascii:char>");
     	}
		if (this.mode == DEBUGMODE.FOLLOW){Doutstream.append("\n <<found char>><MAX:'min open bufferlength',I:'actual length buffer'>\n\n");}
	    finders = list;
	    init();
		try{
		    while ((charint = input.read()) != -1 ){//TODO debug => if !normal...
	    		if (this.mode == DEBUGMODE.FOLLOW){Doutstream.append("\n <I read='"+(char)charint+"' ><MAXlock:"+maxlockbufferoffinders+",Iclock:"+bufferclockLength+">");}
	    		if (this.mode == DEBUGMODE.CHARINT){Doutstream.append("<"+charint+":"+(char)charint+">");}
	    		if (this.mode == DEBUGMODE.DEBUG){Doutstream.append(maxlockbufferoffinders+":"+bufferclockLength);}
	    		//*****************************************************************************************************
				if (finderobjects(charint)){if (this.mode == DEBUGMODE.FOLLOW){Doutstream.append("<DEBUGtag:found>");}
						try {
			    			if (this.mode == DEBUGMODE.DEBUG){	Doutstream.append(finder.toString()+new String(buffer));		}
			    			int temp = bufferclockLength - finder.getusedbufferlength();
			    			if (finder.getusedbufferlength()!=bufferclockLength){//it for when a other finder locked more of the buffer
			    				Doutstream.append(new String(trimArray(buffer,0,temp)));//loss buffer before used buffer of found
			    			}
			    			
			    			finder.convert(trimArray(buffer,0,bufferclockLength-temp),Doutstream);
			    			init();
						}catch (IOException e) {
							throw new RuntimeException("IOExetion:=>"+ e.getMessage());
						}catch (Exception e) {
							throw new RuntimeException("Converter:=>findtostring"+ finder.toString()+"buffer:"+new String(buffer)+ e.getMessage());
						}
				//******************************************************************************************************
		    	}else if(maxlockbufferoffinders==0 ){
		    		if (this.mode == DEBUGMODE.FOLLOW){  
		    			if(Level.FINE != null){
		    				Doutstream.append("\n <I write='"+(char)charint+"' ><DEBUGtag:null>");
		    			}else{
		    				Doutstream.append("'"+(char)charint+"'");
		    			}			
		    		}
		    		if (this.mode == DEBUGMODE.DEBUG){	Doutstream.append("<0>");}
		    		Doutstream.append(new String(trimArray(buffer,0,0)));
		    		init();
		    	//******************************************************************************************************
		        }else if(maxlockbufferoffinders<bufferclockLength ){
		    		if (this.mode == DEBUGMODE.FOLLOW){if(Level.FINE != null)Doutstream.append("\n<DEBUGtag:resize>");}
		    		if (this.mode == DEBUGMODE.DEBUG){if(Level.FINE != null)Doutstream.append("<r>");}
		    		Doutstream.append(new String(trimArray(buffer,0,bufferclockLength - maxlockbufferoffinders)));//LEFTTRIM
		    		buffer = trimArray(buffer, bufferclockLength - maxlockbufferoffinders, buffer.length - maxlockbufferoffinders-bufferclockLength);//
		    		bufferclockLength =maxlockbufferoffinders;
		    	}
				Doutstream.flush();
		    }
			}catch(StringIndexOutOfBoundsException e){
				//System.out.println("ERROR OUT OF BOUND");			
			}
	}
	/**
	 *@param input is the buffer for trimming
	 *@param start is start at 0 or ...
	 *@param length when it is 0 he wil search to (value 0) charS
	 * */
	private char[] trimArray(char [] input,int start,int length){
		int mlength = 0;
		if (length>0){
			mlength = length;
		}else{
			for (char value :input){
				if(value>0){
					mlength++;
				}
			}
		}
		char result[] = new char[mlength];
		try{//debug error carry return line windows
			if (this.mode == DEBUGMODE.FOLLOW){System.out.println("\n<trimArray  input='"+new String(input)+"',start='"+start+"'lenght='"+mlength+"' >");}
		    System.arraycopy(input, start, result,0, mlength);
		}catch (ArrayIndexOutOfBoundsException e) {
			if (this.mode == DEBUGMODE.FOLLOW){System.out.println("\n<trimArray OUTofBound input='"+input.toString()+"',start='"+start+"'lenght='"+mlength+"' >");}
		    System.arraycopy(input, start, result,0, mlength-1);
		}
		return result;
	}
	/**
	 * wil make the buffer bigger see buffersizeSequence
	 * */
	private void make_buffer_bigger(){
		char bufferEX[] = new char[this.buffer.length + buffersizeSequence];
		System.arraycopy(this.buffer, 0, bufferEX, 0, buffer.length);//arraycopy(Object srcarray, int srcPos,Object destarray,int destPos,int length)
		this.buffer = bufferEX;
	}
	/**
	 * @param achar is the next found char that we test for all finders
	 * @return boolean when a positief match is found
	 * */
	private boolean finderobjects(int achar){	
		boolean ischarfound = false;
		int i = 0 ;
		try{
			buffer[bufferclockLength]=(char)achar;
			if(buffer.length == bufferclockLength){
				make_buffer_bigger();
			}
			bufferclockLength++;
		}catch (ArrayIndexOutOfBoundsException e) {
			make_buffer_bigger();
			buffer[bufferclockLength]=(char)achar;
			bufferclockLength++;
		}
		for (Iterator<Finders> iter = finders.iterator(); iter.hasNext();) {//Generics No casting =>faster
            finder = iter.next();
            if(finder.isnext(achar)){//equals char
                ischarfound =true;
            }
            if(finder.ismatchfound()){//has match an 
            	ismatch = true;
            	return true;
            }
        }
			if (ischarfound){//it means that in one or more the a piece match was
	            if (maxlockbufferoffinders<bufferclockLength){maxlockbufferoffinders = bufferclockLength;
	            	if (this.mode == DEBUGMODE.FOLLOW){System.out.println("\n\t<SETMAXBUFFER findername='"+finder.getClass().getName()+"' lenght='"+bufferclockLength+"'>"+finder.toString());}
	            }
			}else{//no finder has found a piece of a match
				maxlockbufferoffinders = 0;//reset of lock => buffer lost data
			}
		return false;
	}
    private static  void log(Level level, String msg ){
        String tag ="<>>>>*"+be.djdb.Statics.LIBNAME+"-"+be.djdb.Statics.COPYRIGHTBY+"*<<<<>";
        Logger.getLogger(StreamMultiManipulator.class.getName()).log(level, tag + msg);
    }
}